class AI
  ROUND_START_DELAY = 60
  RUN_AWAY_TIME = 50
  RIFLE_TRY_TIME = 200
  GRENADE_RANGE = 500
  GRENADE_CHANCE = 8
  MIN_RIFLE_CHASE_RANGE = 200
  GrenadeLauncher_MIN_ACCURACY = 50
  EVADE_WHILE_RELOADING = :no
  MIN_RELOAD_CHASE_RANGE = 200
  
  def initialize(unit)
    @unit = unit
  end
  
  def notify_reloading
    case EVADE_WHILE_RELOADING
      when :towards
        distance = @target.x - @unit.x
        if distance.abs > MIN_RELOAD_CHASE_RANGE
          @reload_run_direction = (distance <=> 0)
        else
          @reload_run_direction = (0 <=> distance)
        end
      when :away
        @reload_run_direction = -1 * towards_target
      when :random
        @reload_run_direction = rand - 0.5 <=> 0
      else
        @reload_run_direction = 0
    end
    DebugOut.out "reload_run_direction: #{@reload_run_direction}"
  end
  
  def new_round
    @lifetime = 0
    @time_since_rifle = 0
    @reload_run_direction = 0
    @back_off_from_wall = 0
    @prev_health, @run_away_timer = 100, 0
    @targets = $game.units.reject { |u| u.team == @unit.team  }
    @teammates = $game.units.reject { |u| u.team != @unit.team }
    switch_weapon_to("GrenadeLauncher")
  end
  def get_update
    @lifetime += 1
    
    @target = nearest_target if @lifetime == ROUND_START_DELAY - 1 or @target == nil or @target.dead
    
    @reload_run_direction = 0 if @unit.selected_weapon.reload_timer < 1
    
    if @unit.health < @prev_health and !@unit.poisoned
      #we've been hit
      @run_away_timer = RUN_AWAY_TIME unless @run_away_timer > 0
    end
    @prev_health = @unit.health
    x_distance_to_cover = @target.x - @unit.x
    y_distance_to_cover = @target.y - @unit.y
    if @run_away_timer > 0
      @run_away_timer -= 1
      if x_distance_to_cover > 0
        return -1, 0, 0
      else
        return 1, 0, 0
      end
    end
    if @unit.selected_weapon.reload_timer == 0 and @back_off_from_wall == 0
      if x_distance_to_cover > 0 and @unit.direction != :right
        return 1, 0, 0
      elsif x_distance_to_cover < 0 and @unit.direction != :left
        return -1, 0, 0
      end
    end
    
    #DebugOut.out "@time_since_rifle #{@time_since_rifle}"
    if @time_since_rifle > RIFLE_TRY_TIME or @unit.selected_weapon.class.to_s == "Rifle"
      @preferred_weapon = "Rifle"
    else
      if x_distance_to_cover.abs < GRENADE_RANGE
        if rand(1000) < GRENADE_CHANCE or (@unit.selected_weapon.reload_timer > 0 and @unit.selected_weapon.class.to_s == "Grenades")
          @preferred_weapon = "Grenades"
          @time_since_rifle += 1
        else
          @preferred_weapon = "GrenadeLauncher"
          @time_since_rifle += 1
        end
      else
        @preferred_weapon = "GrenadeLauncher"
        @time_since_rifle += 1
      end
    end
    switch_weapon_to(@preferred_weapon)
    
    move_x = weapon_angle_change = 0
    case @preferred_weapon
      when "Grenades"
        fire #kind of half-assing this one
      when "GrenadeLauncher"
        @fire_over_45 = true if teammate_in_low_grenade_launcher_arc?(@unit.direction)
        #@unit.weapon_angle = 100
        expected_speedx = offset_x(@unit.weapon_angle, @unit.selected_weapon.speed) + @unit.dx
        expected_speedy = -offset_y(@unit.weapon_angle, @unit.selected_weapon.speed) - @unit.dy
        
        if expected_speedy > 0
          time_to_zenith = expected_speedy / Game::GRAVITY
          zenith_down_speed = 1
        else
          time_to_zenith = 0
          zenith_down_speed = -expected_speedy
          if zenith_down_speed > -1 and zenith_down_speed < 1
            zenith_down_speed = 1
          end
        end
        
        zenith_height = @unit.y + time_to_zenith * expected_speedy / 2
        if zenith_height > @target.y
          #DebugOut.out (zenith_height - @target.y) / Game::GRAVITY * 2 / zenith_down_speed
          begin
            time_to_impact = time_to_zenith + Math.sqrt((zenith_height - @target.y) / Game::GRAVITY * 2 / zenith_down_speed) - 0.5
          rescue
            DebugOut.out "time_to_zenith + Math.sqrt((zenith_height - @target.y) / Game::GRAVITY * 2 / zenith_down_speed) - 0.5"
            DebugOut.out "#{time_to_zenith} + Math.sqrt((#{zenith_height} - #{@target.y}) / #{Game::GRAVITY} * 2 / #{zenith_down_speed}) - 0.5"
            DebugOut.out "#{time_to_zenith} + Math.sqrt((#{zenith_height - @target.y}) / #{Game::GRAVITY} * #{2 / zenith_down_speed}) - 0.5"
            DebugOut.out $!
            raise
          end
          increase_angle_override = false
          fire_override = false
        else
          time_to_impact = x_distance_to_cover / expected_speedx
          ###rethink this
          increase_angle_override = true
          fire_override = false ###true
        end
        expected_distance = time_to_impact * expected_speedx
        #expected_distance = 0 if @unit.weapon_angle > 90 or @unit.weapon_angle < -90
        
        if expected_distance.abs < x_distance_to_cover.abs
          weapon_angle_change = towards_45
        else
          weapon_angle_change = away_from_45
        end
        
        if increase_angle_override
          weapon_angle_change = 1
        end
        #DebugOut.out "expected_distance #{expected_distance}, x_distance_to_cover #{x_distance_to_cover}, angle: #{@unit.weapon_angle}, angle_change: #{weapon_angle_change}"
        
        #DebugOut.out "if expected_distance < x_distance_to_cover + GrenadeLauncher_MIN_ACCURACY and expected_distance > x_distance_to_cover - GrenadeLauncher_MIN_ACCURACY"
        #DebugOut.out "if #{expected_distance} < #{x_distance_to_cover} + #{GrenadeLauncher_MIN_ACCURACY} and #{expected_distance} > #{x_distance_to_cover} - #{GrenadeLauncher_MIN_ACCURACY}: #{expected_distance < x_distance_to_cover + GrenadeLauncher_MIN_ACCURACY and expected_distance > x_distance_to_cover - GrenadeLauncher_MIN_ACCURACY}"
        #DebugOut.out "zenith height: #{zenith_height}, @target.y: #{@target.y}, time_to_impact: #{time_to_impact}, zenith_down_speed: #{zenith_down_speed}"
        #DebugOut.out "weapon_angle: #{@unit.weapon_angle}, towards_45: #{expected_distance.abs < x_distance_to_cover.abs}, increase_angle_override: #{increase_angle_override}"
        #DebugOut.out
        if (expected_distance < x_distance_to_cover + GrenadeLauncher_MIN_ACCURACY and expected_distance > x_distance_to_cover - GrenadeLauncher_MIN_ACCURACY) or fire_override
          unless teammate_in_low_grenade_launcher_arc?(@unit.direction) and under_45?
            fire
          end
        elsif expected_distance.abs < x_distance_to_cover.abs and at_45 then
          switch_weapon_to("Rifle") if @time_since_rifle > RIFLE_TRY_TIME / 2
          move_x = x_distance_to_cover <=> 0
        end
        
        if @fire_over_45
          case @unit.direction
            when :left
              if @unit.weapon_angle < -44
                weapon_angle_change = 1
              else
                @fire_over_45 = false
              end
            when :right
              if @unit.weapon_angle > 44
                weapon_angle_change = 1
              else
                @fire_over_45 = false
              end
          end
        end
      when "Rifle"
        best_angle = angle(@unit.x, -@unit.y, @target.x, -@target.y)
        if best_angle > 180
          best_angle -= 360
        end
        if @unit.weapon_angle == best_angle.to_i
          fire
        else
          weapon_angle_change = best_angle <=> @unit.weapon_angle
          weapon_angle_change *= -1 if @unit.direction == :right
          #DebugOut.out "best_angle #{best_angle}, weapon angle #{@unit.weapon_angle}, weapon_angle_change #{weapon_angle_change}, @unit.y #{@unit.y}, @target.y #{@target.y}"
        end
        move_x = x_distance_to_cover <=> 0 if x_distance_to_cover.abs > MIN_RIFLE_CHASE_RANGE
    end
    
    move_x = @reload_run_direction if @reload_run_direction and move_x == 0
    if @back_off_from_wall > 0
      move_x = -1 * towards_target
      @back_off_from_wall -= 1
    end
    return move_x, weapon_angle_change, 0
  end
  
  def towards_45
    case @unit.direction
      when :left
        if @unit.weapon_angle > -45
          -1
        else
          1
        end
      when :right
        if @unit.weapon_angle > 45
          1
        else
          -1
        end
    end
  end
  def away_from_45
    case @unit.direction
      when :left
        if @unit.weapon_angle > -45
          1
        else
          -1
        end
      when :right
        if @unit.weapon_angle > 45
          -1
        else
          1
        end
    end
  end
  
  def at_45
    case @unit.direction
      when :left
        return true if @unit.weapon_angle <= -44 and @unit.weapon_angle >= -46
      when :right
        return true if @unit.weapon_angle >= 44 and @unit.weapon_angle <= 46
    end
    return false
  end
  
  def under_45?
    case @unit.direction
      when :left
        if @unit.weapon_angle < -45
          return true
        end
      when :right
        if @unit.weapon_angle > 45
          return true
        end
    end
  end
  
  def towards_target
    ((@target.x - @unit.x) <=> 0)
  end
  
  def switch_weapon_to(weapon)
    until @unit.selected_weapon.class.to_s == weapon or (weapon == "GrenadeLauncher" and @unit.selected_weapon.class.to_s == "PoisonGrenadeLauncher")
      @unit.switch_weapon(1)
    end
  end
  
  def notify_landing(missile)
    if missile.class.to_s == "RifleRound"
      switch_weapon_to("GrenadeLauncher")
      @time_since_rifle = 0
    elsif missile.class.to_s == "GrenadeLauncherRound"
      #if it was less than 75% of the x distance to the target, and weapon angle was less than 45 degrees, move to the higher 45+ arc
      distance_to_target = (@unit.x - @target.x).abs
      missile_miss_distance = (@target.x - missile.x).abs
      if missile_miss_distance > distance_to_target * 0.8 and under_45?
        DebugOut.out "go high"
        @fire_over_45 = true
      end
    end
  end
  
  def notify_off_screen(missile)
    if missile.class.to_s == "RifleRound"
      switch_weapon_to("GrenadeLauncher")
      @time_since_rifle = 0
    end
  end
  
  def notify_hit(victim)
    if victim.team == @unit.team
      if @unit.selected_weapon.class.to_s == "Rifle"
        switch_weapon_to("GrenadeLauncher")
        @time_since_rifle = 0
      end
      DebugOut.out "Sorry!" if victim.human?
    end
  end
  
  def nearest_target
    min_distance = -1
    @targets.each_with_index do |t, i|
      if !t.dead and ((t.x - @unit.x).abs <= min_distance or min_distance == -1)
        min_distance = (t.x - @unit.x).abs
        @target = t
      end
    end
    @target
  end
  
  def fire
    return false if @target.dead or @lifetime < ROUND_START_DELAY
    if @unit.selected_weapon.class.to_s == "GrenadeLauncher" #don't fire point blank into a wall...this could be improved
      if !$game.map.would_fit(@unit.x + offset_x(@unit.weapon_angle, @unit.selected_weapon.ammo_type.splash_radius?), @unit.y - offset_y(@unit.weapon_angle, @unit.selected_weapon.ammo_type.splash_radius?), 5, 5)
        @back_off_from_wall = @unit.selected_weapon.ammo_type.splash_radius?
        return false
      end
    end
    @unit.fire
  end
  
  def teammate_in_low_grenade_launcher_arc?(dir)
    case dir
      when :left
        @teammates.each do |t|
          if t.x < @unit.x and t.y + t.height >= @unit.y and !t.dead
            return true
          end
        end
      when :right
        @teammates.each do |t|
          if t.x > @unit.x and t.y + t.height >= @unit.y and !t.dead
            return true
          end
        end
    end
    false
  end
end